var net = require('net');
var url = require('url');
var util = require('util');
var dgram = require('dgram');
function range (val) {
    return val.split('..').map(Number);
}

function list (val) {
    return val.split(',')
}
Date.prototype.Format = function (fmt) { //author: meizz 
    var o = {
        "M+": this.getMonth() + 1, //月份 
        "d+": this.getDate(), //日 
        "h+": this.getHours(), //小时 
        "m+": this.getMinutes(), //分 
        "s+": this.getSeconds(), //秒 
        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
        "S": this.getMilliseconds() //毫秒 
    };
    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    for (var k in o)
        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
    return fmt;
}
//定义参数,以及参数内容的描述
//program
//    .version('0.0.1')
//    .usage('[options] [value ...]')
    //.option('-m, --protocol <string>', '协议')
    //.option('-i, --ipaddr <string>', 'ip地址')
//    .option('-p, --port <integer>', '端口',parseInt)
//    .option('-l, --list <items>', 'a list', list)
//    .option('-r, --range <a>..<b>', 'a range', range)

//添加额外的文档描述
//program.on('help', function() {
//    console.log('   Examples:')
//    console.log('')
//    console.log('       # input port')
//    console.log('       $ ./cc.js -p 10001')
//    console.log('')
//});

//解析commandline arguments
//program.parse(process.argv);

//输出结果
//console.info('protocol:')
//console.log(program.port);


function calculate_head_len(protocol){
    var headlen = 0;
	if(protocol.start != null)
		headlen += protocol.start.len;

	headlen += protocol.deviceid.len; 
    headlen += protocol.contentlen.len ;
    headlen += protocol.cmd.len;
    if (protocol.headfields != null)
        headlen += protocol.headfields.len;
    //console.log("the headlen is", headlen);
    return headlen;
}

function calculate_tail_len(protocol){
	var taillen = 0;
    if(protocol.crc!=null)
		taillen += protocol.crc.len; //这个在尾部
    if(protocol.end!=null)
        taillen += protocol.end
    return taillen;
}

function cal_max_body_len(protocol){
    var max = calculate_head_len(protocol); 
    max += calculate_tail_len(protocol);
    if(protocol.contentlen.len == 1)
        max += 256;
    else if(protocol.contentlen.len == 2)
        max += 65536;
    else 
        max += 65536*2; //最多两个64k分片长度
    return max;
}




function ProcessUdpServer(protocol, port, callback) {

    var server = dgram.createSocket('udp4');
    server.on('error', function (err) {
        console.log("udp server error:\n", err);
        server.close();
    });

    //处理子协议头部
    function process_bele_field_content_child_1(buff, fields, pos) {
        var obj = {};
        obj.head = {};
        var spos = pos;
        // console.log("process the data of child_1 :", spos);
        var total_len = 0;
        //var len = 0;
        var bitlen = 0; for (var i = 0; i < fields.length; i++) {
            var type = fields[i].type;
            var ftype = fields[i].ftype;
            var flen = fields[i].len;
            var name = fields[i].name;
            var len = 0;
            var value = null;

            switch (type) {
                case "int8":
                    len += 1;
                    value = buff[spos];
                    break;
                case "bit": {
                    bitlen += flen;
                    if (bitlen == 8) {
                        len += 1;
                        bitlen = 0;
                    }
                    var bitoffset = parseInt(ftype);//位偏移
                    var offset = spos; //字节偏移
                    var bv = buff[offset];
                    //var t1 = (Math.pow(2, 9 - bitoffset) - 1);
                    //var t2 = (9-bitoffset-flen );
                    //console.log("bv,t1", bv, t1, t2);
                    value = bv & (Math.pow(2, 9 - bitoffset) - 1);
                    value = value >> (9 - bitoffset - flen);

                    //return value;
                }
                    break;
                case "int16":
                    value = buff.readInt16BE(spos);
                    len += 2;
                    break;
                case "int32":
                    value = buff.readInt32BE(spos);
                    len += 4;
                    break;
            }
            if (bitlen == 0)
                spos += len;
            total_len += len;
            obj.head[name] = value;
        }
        obj.headlen = total_len;
        //console.log("the obj is :", obj);
        return obj;
    }
    //处理数据
    function process_bele_field_content_child_2(buff, fieldobj, pos) {
        var k = 1;
        var num = 0;
        var len = 0;
        var point = 0;
        var spos = pos;
        var value = [];
        ///console.log(fieldobj);
        for (var key in fieldobj.head) {
            if (k == 1)
                num = parseInt(fieldobj.head[key]);
            else if (k == 2) {
                len = parseInt(fieldobj.head[key]);
                // break;
            }
            else if (k == 3) {
                point = parseInt(fieldobj.head[key]);
            }
            //else if( k == 4)
            k++;
        }
        var tlen = num * len;
        //console.log("tlen is: ", tlen);
        if (tlen <= 0) {
            console.log("error len");
            return 0;
        }
        else {

            for (var i = 0; i < num; i++) {
                if (len == 1) {
                    value.push(buff[spos]);
                }
                else if (len == 2) {
                    value.push(buff.readInt16BE(spos));
                }
                else if (len == 4) {//四

                    //console.log("the point is " + point);
                    //if(point > 0 ){ //如果小数位数大于0,说明是浮点数
                    //var te= buff.readFloatBE(spos);
                    //var te2 = buff.readFloatLE(spos);
                    //console.log("the value is : "+te +" " + te2);
                    value.push(buff.readFloatBE(spos));
                    //}
                    //else{
                    //否则就是整数
                    //  value.push(buff.readUInt32BE(spos));
                    //}
                }
                spos += len;
            }//for end
        }//else
        fieldobj.value = value;
        //返回处理数据的长度
        return tlen;
    }

    //处理子协议函数 ftype == leftcycle 剩余字节循环
    function process_bele_field_content_child(buff, field_child, pos, leftdatalen) {
        //buf长度
        var len = buff.length;
        var left_len = leftdatalen; //剩余所有的字节数目

        var spos = pos;
        var data = [];

        while (left_len > 0) {
            //                console.log("the left_len is :", left_len,len , pos);
            var fieldobj = process_bele_field_content_child_1(buff, field_child, spos);
            var headlen = fieldobj.headlen;
            spos += headlen; //处理头部长度
            var contentlen = process_bele_field_content_child_2(buff, fieldobj, spos);
            if (contentlen == 0)
                return data;
            spos += contentlen;

            left_len -= contentlen;
            left_len -= headlen;
            data.push(fieldobj);
        }
        return data;
    }
    //增加处理子协议,leftdatalen 剩余需要分析的字节数
    function get_obj_field_value(buff, fieldobj, pos, leftdatalen) {
        var type = fieldobj.type;
        var len = fieldobj.len;
        var ftype = fieldobj.ftype;
        var name = fieldobj.name;
        var value = null;
        if (type == "float") {
            if (ftype == "bige")
                value = buff.readFloatBE(pos);
            else
                value = buff.readFloatLE(pos);
        }
        else if (type == "double") {
            if (ftype == "bige")
                value = buff.readDoubleBE(pos);
            else
                value = buff.readDoubleLE(pos);
        }
        else if (type == "int16") {
            if (ftype == "bige")
                value = buff.readInt16BE(pos);
            else
                value = buff.readInt16LE(pos);
        }
        else if (type == "uint16") {
            if (ftype == "bige")
                value = buff.readUInt16BE(pos);
            else
                value = buff.readUInt16LE(pos);
        }
        else if (type == "int32") {
            if (ftype == "bige")
                value = buff.readInt32BE(pos);
            else
                value = buff.readInt32LE(pos);
        }
        else if (type == "uint32") {
            if (ftype == "bige")
                value = buff.readUInt32BE(pos);
            else
                value = buff.readUInt32LE(pos);
        }
        else if (type == "uint8") {
            value = buff.readUInt8(pos);
        }
        else if (type == "int8") {
            value = buff.readInt8(pos);
        }
        else if (type == "string") {
            value = buff.toString('utf8', pos, pos + len);
        }
        else if (type == "time6") {
            var timestr = "";
            timestr = new Date().Format('yyyy-MM-dd hh:mm:ss');
            if (0) {
                value1 = buff[pos];
                value1 += 2000;
                //if(value1 > 2017)
                timestr += value1;
                timestr += "-";
                timestr += buff[pos + 1];//月
                timestr += "-";
                timestr += buff[pos + 2];//日
                timestr += " ";
                timestr += buff[pos + 3];
                timestr += ":";
                timestr += buff[pos + 4];
                timestr += ":";
                timestr += buff[pos + 5];
            }
            return timestr;

        }
        else if (type == "protocol" && ftype == "leftcycle") {//子协议处理

            // const realdata = Buffer.allocUnsafe(datalen);
            // buff.copy(realdata, 0, hlen, hlen + datalen);
            if (fieldobj.field_child != null)
                value = process_bele_field_content_child(buff, fieldobj.field_child, pos, leftdatalen);
            else
                value = null;
            //console.log(value);
        }
        return value;
    }
    ///fields 是bodyfields headfields 就是头部field
    //datalen : 剩余需要分析的数据长度
    function process_bele_field_content(fields, buff, startpos, cmd, datalen) {
        //var bodypos = ;
        //位置加上头部字节
        var pos = startpos + hlen;
        var retobj = {};
        retobj.field = {};
        var buflen = buff.length;
        var leftdatalen = datalen;
        for (var i = 0; i < fields.length; i++) {
            // if(pos + datalen >= )
            var value = get_obj_field_value(buff, fields[i], pos, leftdatalen);
            if (value != null) {
                var name = fields[i].name;
                if (fields[i].type == "protocol" && fields[i].ftype == "leftcycle")
                    retobj[name] = value;
                else
                    retobj.field[name] = value;
            }
            pos += fields[i].len;
            //计算剩余的字节数目
            leftdatalen -= fields[i].len;

        }//for end

        retobj.cmd = cmd;
        if (protocol.headfields == null || protocol.headfields.field == null)
            return retobj;
        var flength = protocol.headfields.field.length;
        if (flength == 0)
            return retobj;
        var h_field = protocol.headfields.field;
        //console.log("jjj",h_field,h_field.length);
        //得到第一个偏移量
        var first_offset = h_field[0].offset;
        // console.log("first_offset", first_offset);
        ////////////////////////////////////
        ////////
        //继续写
        pos = startpos + first_offset - 1;
        retobj.headfield = {};// [];
        //计算

        for (var j = 0; j < h_field.length; j++) {
            // console.log("h_field",h_field[j]);
            var value = get_obj_field_value(buff, h_field[j], pos);
            if (value != null) {
                var name = h_field[j].name;
                //var headobj = {};
                //headobj[name] = value;
                //headobj.name = h_field[j].name;
                //headobj.value = value;
                retobj.headfield[name] = value; //.push(headobj);
            }
            pos += h_field[j].len;
        }
        return retobj;
    }


    function process_packet_1(buff, startpos, datalen) {
        //回复指令,不通用,udp 不使用
        if (0) {
            var retbuffer = Buffer.allocUnsafe(18);
            buff.copy(retbuffer, 0, 0, 18);
            retbuffer.writeInt16BE(18, 0); //头部写入大端18字节
            socket.write(retbuffer);
        }
        //回复指令,不通用
        //  console.log("the datalen is %d", datalen);
        var cmdoffset = protocol.cmd.offset + startpos;
        var cmdlen = protocol.cmd.len;
        var cmd = null;
        if (cmdlen == 1)
            cmd = buff[cmdoffset - 1];
        else if (cmdlen == 2)
            cmd = buff.readInt16BE(cmdoffset - 1);
        else if (cmdlen == 4)
            cmd = buff.readInt32BE(cmdoffset - 1);
        //console.log("original data:", buff);
        // var temp = util.format("the protocol cmd is %d", cmd);
        // console.log(temp);
        var str_deviceid = "";
        var idpos = protocol.deviceid.offset - 1 + startpos;

        str_deviceid = buff.toString('hex', idpos, idpos + protocol.deviceid.len);
        temp = util.format("the deviceid is %s", str_deviceid);
        console.log(temp);

        //从命令表中获取字段,如果字段不是固定的,需要使用外部特殊处理
        var fields = protocol_map.get(cmd);
        if (fields == null) {
            temp = util.format("---error! the cmd is %d,but in protocol not find this command---", cmd);
            console.log(temp);
            //return;
        }
        else {
            //计算字段需要的长度
            var clen = 0;
            for (var i = 0; i < fields.length; i++) {
                clen += fields[i].len;
            }
            //这里可能不正确,涉及到数据字段是否包含二次协议
            if (datalen != clen) {
            }

            //从协议中获取所有字段的值并且生成对象
            //datalen 需要分析的数据长度
            var retobj = process_bele_field_content(fields, buff, startpos, cmd, datalen);

            if (callback != null && retobj != null) {
                retobj.deviceid = str_deviceid; //deviceid 赋值
                callback(retobj);
            }
        }


    }

    server.on('message', function (buffer) {
        //console.log("server got: ", msg);
        if (buffer != null) {
            process_packet_1(buffer, 0, buffer.length);
        }
    });

    server.on('listening', function () {
        const address = server.address();
        console.log("server listening ${address.address}:${address.port}");
    });

    server.bind(port);
}


exports.ProcessUdpServer = ProcessUdpServer;

exports.ProcessTcpServer = ProcessTcpServer;